/**
 * Copyright (C), 2015-2019, XXX有限公司
 * FileName: DynamicDataSourceConfig
 * Author:   wh
 * Date:     2019/4/30 12:10
 * Description:
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package io.imp.dynamic.datasource.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSON;
import com.zaxxer.hikari.HikariDataSource;
import io.imp.common.utils.DESedeUtils;
import io.imp.dynamic.datasource.constant.DataSourceConstant;
import io.imp.dynamic.datasource.properties.DataSourceProperties;
import io.imp.dynamic.datasource.properties.DynamicDataSourceProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @program: imp-security
 * @ClassName: DynamicDataSourceConfig
 * @Description:
 * @author: wanghui
 * @date: 2019/4/30 12:10
 * @Version: 1.0.0
 */
@Slf4j
@EnableConfigurationProperties({DataSourceProperties.class,DynamicDataSourceProperties.class})
@Service
public class DynamicDataSourceConfig implements TransactionManagementConfigurer {
    private final Map<Object, Object> dataSourceMap = new HashMap<>(8);
    @Autowired
    private DataSourceProperties dataSourceProperties;
    @Autowired
    private DynamicDataSourceProperties dynamicDataSourceProperties;

    @Bean("dynamicDataSource")
    public DynamicDataSource dataSource() {
        log.info("开始 -> 初始化默认数据源");
        DynamicDataSource ds = new DynamicDataSource();
        HikariDataSource cads = new HikariDataSource();
        cads.setJdbcUrl(dataSourceProperties.getUrl());
        cads.setDriverClassName(dataSourceProperties.getDriverClassName());
        cads.setUsername(dataSourceProperties.getUsername());
        cads.setPassword(dataSourceProperties.getPassword());
        ds.setDefaultTargetDataSource(cads);
        dataSourceMap.put(0, cads);
        dataSourceMap.put("default", cads);
        getDynamicDataSource();
        ds.setTargetDataSources(dataSourceMap);
        return ds;
    }

    private void getDynamicDataSource(){
        Map<String, DataSourceProperties> dataSourcePropertiesMap = dynamicDataSourceProperties.getDatasource();
        dataSourcePropertiesMap.forEach((k, v) -> {
            DruidDataSource druidDataSource = DynamicDataSourceFactory.buildDruidDataSource(v);
            dataSourceMap.put(k, druidDataSource);
        });
    }
    /**
     * 组装默认配置的数据源，查询数据库配置
     */
    @PostConstruct
    public void init() {
        DriverManagerDataSource dds = new DriverManagerDataSource();
        dds.setUrl(dataSourceProperties.getUrl());
        dds.setDriverClassName(dataSourceProperties.getDriverClassName());
        dds.setUsername(dataSourceProperties.getUsername());
        dds.setPassword(dataSourceProperties.getPassword());

        List<Map<String, Object>> dbList = new JdbcTemplate(dds).queryForList(DataSourceConstant.QUERY_DS_SQL);
        log.info("开始 -> 初始化动态数据源");
        Optional.of(dbList).ifPresent(list -> list.forEach(db -> {
            log.info("数据源:{}", db.get(DataSourceConstant.DS_NAME));
            HikariDataSource ds = new HikariDataSource();
            ds.setJdbcUrl((String)db.get(DataSourceConstant.DS_JDBC_URL));
            ds.setDriverClassName((String)db.get(DataSourceConstant.DS_DRIVER_CLASS));
            ds.setUsername((String) db.get(DataSourceConstant.DS_USER_NAME));

            String decPwd = DESedeUtils.decrypt(db.get(DataSourceConstant.DS_USER_PWD).toString());
            ds.setPassword(decPwd);
            dataSourceMap.put(db.get(DataSourceConstant.DS_NAME), ds);
        }));

        log.info("完毕 -> 初始化动态数据源,共计 {} 条", dataSourceMap.size());
    }

    /**
     * 重新加载数据源配置
     */
    public Boolean reload() {
        dataSourceMap.clear();
        init();
        DynamicDataSource dataSource = dataSource();
        log.info("数据源配置：{}", dataSource);
        dataSource.afterPropertiesSet();
        return Boolean.FALSE;
    }


    @Bean
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return txManager();
    }
}